mod container;
mod image;
mod models;
mod network;

use anyhow::Result;
use container::*;
use image::*;
pub use models::*;
use network::*;
use tokio::fs;

impl DeployManager {
    pub async fn start_deploy(&self) -> Result<()> {
        println!("--------deploy start--------");

        if let Some(network) = self.config.base.network.clone() {
            create_network(&self.docker, network).await.map_err(|e| {
                println!("create network fail: {}", e);
                e
            })?;
        }

        let network = self.config.base.network.clone();

        for dep in &self.config.deps {
            let name = dep.name.clone();

            if let Some(container) = is_container_exist(&self.docker, &name).await? {
                if let Some(state) = container.state {
                    if state == "running" {
                        continue;
                    }
                }
                remove_container(&self.docker, &name).await?;
            }

            let res = create_container(
                &self.docker,
                dep.clone(),
                &self.config.curr_path,
                network.clone(),
            )
            .await?;

            start_container(&self.docker, &name).await?;

            if let Some(cmd) = dep.after_cmd.clone() {
                exec_container(&self.docker, &name, cmd).await?;
            }

            println!(
                "deploy container: {}, id: {}, warnings: {:?}",
                name, res.id, res.warnings
            );
        }

        println!("--------deploy finished--------");
        Ok(())
    }

    pub async fn update_deploy(&self, name: String) -> Result<()> {
        remove_container(&self.docker, &name).await?;

        let dep = self
            .config
            .deps
            .iter()
            .find(|dep| dep.name == name)
            .cloned();

        if let Some(dep) = dep {
            let res = create_container(
                &self.docker,
                dep,
                &self.config.curr_path,
                self.config.base.network.clone(),
            )
            .await?;

            start_container(&self.docker, &name).await?;

            println!(
                "update container: {}, id: {}, warnings: {:?}",
                name, res.id, res.warnings
            );
        } else {
            println!("deploy {} not found", name);
        }

        Ok(())
    }

    pub async fn shutdown_deploy(&self) -> Result<()> {
        for dep in &self.config.deps {
            match remove_container(&self.docker, &dep.name).await {
                Ok(_) => println!("remove container {}", dep.name),
                Err(e) => println!("remove container fail: {}", e),
            }
        }
        Ok(())
    }

    pub async fn clean_deploy(&self) -> Result<()> {
        for dep in &self.config.deps {
            match remove_container(&self.docker, &dep.name).await {
                Ok(_) => println!("remove container {}", dep.name),
                Err(e) => {
                    println!("remove container fail: {}", e);
                }
            }

            match remove_image(&self.docker, &dep.image).await {
                Ok(_) => println!("remove image {}", dep.image),
                Err(e) => {
                    println!("remove image fail: {}", e);
                }
            }
        }

        Ok(())
    }

    pub async fn load_images(&self) -> Result<()> {
        let mut dir = fs::read_dir("./images").await?;
        while let Some(entry) = dir.next_entry().await? {
            let path = entry.path();
            if path.is_file() && path.extension().map(|ext| ext == "tar").unwrap_or(false) {
                if let Some(image) = path.to_str() {
                    if let Err(e) = load_image(&self.docker, image).await {
                        println!("load images fail: {}", e);
                    }
                }
            }
        }
        Ok(())
    }
}
